/**
 * Program do ręcznego testowania operacji związanych z obsługą przestrzeni
 * krotek w języku komunikacyjnym linda.
 *
 * Wywołania dla poszczególnych funkcji:
 * linda_test -c  /sciezka                              - tworzy nową przestrzeń krotek
 *
 * linda_test -o  /sciezka [typ_argumentu] [wartosc] .. - wstawia krotkę do przestrzeni
 *   typ_argumentu: i-int, f-float, s-string
 *
 * linda_test -r  /sciezka wzorzec_krotki               - odczyt krotki o podanym wzorcu
     wzorzec_krotki: typ_argumentu operator wartosc_referencyjna
     typ_argumentu: i-int, f-float, s-string
     operator: e (==), g (>), ge (>=), l (<), le (<=)
     wartosc_referencyjna: odpowiednia do typu lub '%' (dowolna wartość)
 *
 * linda_test -i  /sciezka wzorzec_krotki               - odczyt niszczący krotki o podanym wzorcu
 * linda_test -rt /sciezka timeout wzorzec_krotki       - odczyt krotki z timeoutem
 * linda_test -it /sciezka timeout wzorzec_krotki       - odczyt niszczący z timeoutem
 * linda_test -d  /sciezka                              - zamyka podaną przestrzeń krotek
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../Rlinda.h"
#include "../tuple.h"
#include "../pattern.h"

#define ARG_REQUIRED(x)     if(argc<x) { printf("Nieprawidlowe wywolanie linda_test.\n"); exit(1); }

void test_create(char *path);
void test_output(char *path, tuple *tup);
void test_read(char *path, pattern *pat, int timeout);
void test_input(char *path, pattern *pat, int timeout);
void test_destroy(char *path);

pattern*   _parse_pattern_input(int type, int start, int argc, char *argv[]);
tuple* _parse_tuple_input(int argc, char *argv[]);


int main(int argc, char *argv[])
{
    tuple *tup = NULL;
    pattern *pat = NULL;
    
    int timeout;
    
    ARG_REQUIRED(2);
    
    // Obsługa parametrów wywołania
    switch(argv[1][1])
    {      
        case 'c':
            // Tworzenie przestrzeni krotek
            ARG_REQUIRED(3);
            test_create(argv[2]);
            break;
        
        case 'o':
            // Zapis do przestrzeni krotek
            ARG_REQUIRED(5);
            tup = _parse_tuple_input(argc, argv);
            test_output(argv[2], tup);
            break;
            
        case 'r':
            // Odczyt krotki o podanym wzorcu            
            if (argv[1][2] == 't') {
                // odczyt z timeoutem
                ARG_REQUIRED(7);
                pat = _parse_pattern_input(READ_PATTERN, 4, argc, argv);
                
                if ((timeout = atoi(argv[3])) == 0) {
                    printf("Nieprawidlowa wartosc parametru (timeout).\n");
                    exit(1);
                }
                test_read(argv[2], pat, timeout);
            } else {
                ARG_REQUIRED(6);
                pat = _parse_pattern_input(READ_PATTERN, 3, argc, argv);
                test_read(argv[2], pat, 0);
            }
            break;
            
        case 'i':
            // Odczyt niszczący krotki o podanym wzorcu

            if (argv[1][2] == 't') {
                // odczyt niszczący z timeoutem
                ARG_REQUIRED(7);
                pat = _parse_pattern_input(INPUT_PATTERN, 4, argc, argv);
                
                if ((timeout = atoi(argv[3])) == 0) {
                    printf("Nieprawidlowa wartosc parametru (timeout).\n");
                    exit(1);
                }
                test_input(argv[2], pat, timeout);
            } else {
                ARG_REQUIRED(6);
                pat = _parse_pattern_input(INPUT_PATTERN, 3, argc, argv);
                test_input(argv[2], pat, 0);
            }
            break;        

        case 'd':
            ARG_REQUIRED(3);
            test_destroy(argv[2]);
            break;
    }
    
    return 0;
}


/**
 * Tworzy nową przestrzeń krotek o zadanej ścieżce.
 */
void test_create(char *path)
{
    printf("Tworzenie nowej przestrzeni krotek (%s)...", path);
    linda_create(path);
    printf(" OK\n");
}


/**
 * Wstawia do przestrzeni krotek nową krotkę utworzoną na podstawie
 * argumentów wywołania programu.
 */
void test_output(char *path, tuple *tup)
{
    // Przestrzeń krotek.
    linda_space *lspace = NULL;
    
    // Otwarcie przestrzeni krotek.
    lspace = linda_init(path);
    
    if (lspace == NULL) {
        printf("\nPrzestrzen o podanej sciezce (%s) nie istnieje.\n", path);
        exit(1);
    }
    
    // Dodanie krotki do przestrzeni.
    print_tuple(tup);
    
    printf("\nWstawianie krotki do przestrzeni (%s)... ", path);
    fflush(0);
    linda_output(lspace, tup);
    printf(" OK\n");
    
    // Zwolnienie zasobów.
    delete_tuple(tup);
}


/**
 * Próbuje odczytać krotkę o wzorcu pasującym do pobranego z argumentów
 * wywołania programu. Jeżeli nie znaleziono pasującej krotki, to zawiesza
 * się w oczekiwaniu (maksymalnie na czas timeout, jeżeli został podany
 * jako parametr wywołania).
 */
void test_read(char *path, pattern *pat, int timeout)
{
    // Przestrzeń krotek.
    linda_space *lspace = NULL;
    // Wskaźnik do znalezionej krotki.
    tuple *result = NULL;
    
    printf("Otwieranie przestrzeni krotek...");
    lspace = linda_init(path);
    
    if (lspace == NULL) {
        printf("\nPrzestrzen o podanej sciezce (%s) nie istnieje.\n", path);
        exit(1);
    }
    
    printf(" OK\n");
    
    print_pattern(pat);
    
    printf("\nPoszukiwanie krotki o podanym wzorcu w przestrzeni...");
    fflush(0);
    result = linda_read(lspace, pat, timeout);
    
    if (result == NULL) {
        printf("\nUplynal timeout (%d s).\n", timeout);
        return;
    } else {
        printf("\nZnaleziono pasujaca krotke:\n");
        print_tuple(result);
        
        delete_tuple(result);
    }
    
    delete_pattern(pat);
}


/**
 * Próbuje odczytać krotkę o wzorcu pasującym do pobranego z argumentów
 * wywołania programu. Usuwa znalezioną krotkę z przestrzeni. Jeżeli nie 
 * znaleziono pasującej krotki, to zawiesza się w oczekiwaniu (maksymalnie na 
 * czas timeout, jeżeli został podany jako parametr wywołania).
 */
void test_input(char *path, pattern *pat, int timeout)
{
    // Przestrzeń krotek.
    linda_space *lspace = NULL;
    // Wskaźnik do znalezionej krotki.
    tuple *result = NULL;
    
    printf("Otwieranie przestrzeni krotek...");
    lspace = linda_init(path);
    
    if (lspace == NULL) {
        printf("\nPrzestrzen o podanej sciezce (%s) nie istnieje.\n", path);
        exit(1);
    }
    
    printf(" OK\n");
    
    print_pattern(pat);
    
    printf("Poszukiwanie krotki o podanym wzorcu w przestrzeni...\n");
    result = linda_input(lspace, pat, timeout);
    
    if (result == NULL) {
        printf("Uplynal timeout (%d s).\n", timeout);
        return;
    } else {
        printf("Znaleziono pasujaca krotke:\n");
        print_tuple(result);
        
        delete_tuple(result);
    }
    
    delete_pattern(pat);
}


/**
 * Usuwa przestrzeń krotek o podanej ścieżce.
 */
void test_destroy(char *path)
{
    linda_space *lspace = linda_init(path);
    
    if (lspace == NULL) {
        printf("\nPrzestrzen o podanej sciezce (%s) nie istnieje.\n", path);
        exit(1);
    }
    
    printf("Usuwanie przestrzeni krotek (%s)...", path);
    fflush(0);
    linda_destroy(lspace);
    printf(" OK\n");
}


/**
 * Przetwarza argumenty wywołania programu i na ich podstawie tworzy strukturę
 * krotki.
 */
tuple* _parse_tuple_input(int argc, char *argv[])
{
    int i;
    // Wskaźniki na pierwszy i bieżący element krotki.
    element_of_tuple *first = NULL, *cur = NULL;
    
    // Alokacja miejsca dla pierwszego elementu.
    first = malloc(sizeof(element_of_tuple));
    
    // Pobranie par typ-wartość elementów krotki.
    for (i = 3; i < argc; i+=2) {
        data d;
        
        ARG_REQUIRED(i+2);
        
        if (cur != NULL) {
            cur->next = malloc(sizeof(element_of_tuple));
            cur = cur->next;
        } else {
            cur = first;
        }
        
        switch(argv[i][0]) {
            case 'i':
                d._integer = atoi(argv[i+1]);
                cur->type = INTEGER;
                break;
            case 'f':
                d._float = (float)atof(argv[i+1]);
                cur->type = FLOAT;
                break;
            case 's':
                d._string = (argv[i+1]);
                cur->type = STRING;
                break;
        }
        cur->data = d;
    }
    // Ustawienie wskaźnika ostatniego elementu.
    cur->next = NULL;
    
    // Utworzenie krotki.
    tuple* tup = create_tuple(0);
    
    for (cur = first; cur != NULL; cur = cur->next)
        add_elem_to_tuple(tup, *cur);
    
    return tup;
}


/**
 * Przetwarza argumenty wywołania programu i na ich podstawie tworzy strukturę
 * wzorca krotki.
 */
pattern* _parse_pattern_input(int type, int start, int argc, char *argv[])
{
    int i;
    // Wskaźniki na pierwszy i bieżący element wzorca.
    element_of_pattern *first = NULL, *cur = NULL;
    
    // Alokacja pierwszego elementu wzorca
    first = malloc(sizeof(element_of_pattern));
    
    // Pobranie trójek typ-operator-wartość i utworzenie struktury wzorca.
    for (i = start; i < argc; i += 3) {
        data d;
        
        ARG_REQUIRED(i+3);
        
        if (cur != NULL) {
            cur->next = malloc(sizeof(element_of_pattern));
            cur = cur->next;
        } else {
            cur = first;
        }
        
        // Ustawienie typu zmiennej krotki i wartości referencyjnej
        switch (argv[i][0]) {
            case 'i':
                cur->type = INTEGER;
                if (argv[i+2][0] != '%') {
                    cur->valid_data = true;
                    d._integer = atoi(argv[i+2]);
                } else {
                printf("co\n");
                    cur->valid_data = false;
                }
                break;
            case 'f':
                cur->type = FLOAT;
                if (argv[i+2][0] != '%') {
                    cur->valid_data = true;
                    d._float = (float)atof(argv[i+2]);
                } else {
                    cur->valid_data = false;
                }
                break;
            case 's':
                cur->type = STRING;
                if (argv[i+2][0] != '%') {
                    cur->valid_data = true;
                    d._string = argv[i+2];
                } else {
                    cur->valid_data = false;
                }
                break;
        }
     
        // Ustawienie operatora.
        switch (argv[i+1][0]) {
            case 'e':
                cur->comp_oper = EQUAL;
                break;
            case 'l':
                if (strlen(argv[i+1]) > 1)
                    cur->comp_oper = NOT_LARGER;
                else
                    cur->comp_oper = SMALLER;
                break;
            case 'g':
                if (strlen(argv[i+1]) > 1)
                    cur->comp_oper = NOT_SMALLER;
                else
                    cur->comp_oper = LARGER;
                break;
        }
        
        cur->data = d;
    }
    // Ustawienie wskaźnika ostatniego elementu.
    cur->next = NULL;    
    
    // Utworzenie wzorca.
    pattern* pat = create_pattern(0);
    pat->pattern_type = type;
    
    for (cur = first; cur != NULL; cur = cur->next)
        add_elem_to_pattern(pat, *cur);
    
    return pat;
    
}

